from kit.verifier import Verifier
from solver.solver import Solver
from bean.product_dispatch import Dispatch, Period


class Greedy(Solver):
    def __init__(self,
                 product_path="data/产品信息.csv",
                 equip_path="data/设备信息.csv",
                 route_path="data/工艺路线.csv",
                 output_path='data/lx_sol.csv',
                 order=None):
        super().__init__(product_path, equip_path, route_path)
        self.output_path = output_path
        if order is None:
            self.order = [20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
                          30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
                          0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                          10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
        else:
            self.order = order
        self.mp = {'A1': 0, 'B1': 1, 'C1': 2, 'D1': 3}
        self.ready_times = 30
        self.T2_machine = [(0, 0), (100000000, 100000000)]
        self.T3_machine = dict()
        self.T4_machine = dict()
        self.T5_machine = dict()
        for i in range(len(self.data.equips)):
            if self.data.equips[i].equip_type == 'T-03':
                self.T3_machine[self.data.equips[i].equip_name] = [0, -1]
            if self.data.equips[i].equip_type == 'T-04':
                self.T4_machine[self.data.equips[i].equip_name] = [0, -1]
            if self.data.equips[i].equip_type == 'T-05':
                self.T5_machine[self.data.equips[i].equip_name] = [0, [(0, 0), (100000000, 100000000)]]
        self.product_status = list()
        self.T5_task_list = list()

    def is_finished(self, a):
        for i in range(len(a)):
            if a[i][0] != 2:
                return False
        return True

    def is_exist_unstart(self, a):
        flag = False
        for i in range(len(a)):
            if a[i][0] == 0:
                flag = True
        return flag

    def is_tight(self, now_time, product_time, product_idx, product_step):
        tmp_time = self.get_best_end_time(now_time, product_time, product_idx, product_step)
        duration = 12 * self.data.products[product_idx].section_num
        if tmp_time == now_time + duration:
            return 1
        return 0

    def get_best_end_time(self, product_time, product_idx, product_step):
        product = self.data.products[product_idx]
        route = self.data.routes[self.mp[product.route_id]]
        B_duration = route.cost_time_low[product_step]
        C_duration = 12 * product.section_num
        early_time = 10000000
        if route.equip_types[product_step] == 'T-03':
            for name in self.T3_machine:
                pp_time = 30
                if self.T3_machine[name][1] == product_idx:
                    pp_time = 0
                early_time = min(early_time, self.T3_machine[name][0] + pp_time)
        else:
            for name in self.T4_machine:
                pp_time = 30
                if self.T4_machine[name][1] == product_idx:
                    pp_time = 0
                early_time = min(early_time, self.T4_machine[name][0] + pp_time)
        time = max(product_time, early_time) + B_duration
        return time + C_duration

    def get_step_delta(self, product_step, product_idx):
        product = self.data.products[product_idx]
        route = self.data.routes[self.mp[product.route_id]]
        return len(route.cost_time_low) - product_step

    def find_work_product(self):
        pos = -1
        # tight = 0
        end_time = 100000000
        for i in range(len(self.product_status)):
            if self.product_status[i][0] == 1:
                # 是否紧
                # tmp_tight = self.is_tight(now_time, product_time=self.product_status[i][2],
                #                           product_idx=self.product_status[i][3], product_step=self.product_status[i][1])
                # 是否均摊
                # tmp_step_delta = self.get_step_delta(product_step=self.product_status[i][1], product_idx=self.product_status[i][3])
                # 获得最佳结束时间
                tmp_end_time = self.get_best_end_time(product_time=self.product_status[i][2],
                                                      product_idx=self.product_status[i][3],
                                                      product_step=self.product_status[i][1])
                # tmp_end_time = 0
                if tmp_end_time < end_time:
                    pos = i
                    end_time = tmp_end_time
        if pos == -1:
            tmp_pos = -1
            for i in range(len(self.product_status)):
                if self.product_status[i][0] == 0:
                    tmp_pos = i
                    break
            cnt = 0
            for i in range(len(self.product_status)):
                if self.product_status[i][0] == 0:
                    cnt += 1
            if cnt <= 3:
                for i in range(len(self.product_status)):
                    if self.product_status[i][0] == 0:
                        self.product_status[i][0] = 1
            if tmp_pos != -1:
                self.product_status[tmp_pos][0] = 1
                return tmp_pos
        return pos

    def update_status(self, product_status_idx):
        product_idx = self.product_status[product_status_idx][3]
        product_time = self.product_status[product_status_idx][2]
        product_step = self.product_status[product_status_idx][1]
        product = self.data.products[product_idx]
        route = self.data.routes[self.mp[product.route_id]]
        B_duration = route.cost_time_low[product_step]
        C_duration = 12 * product.section_num
        early_time = 10000000
        # 找ready时间最早的机器
        if route.equip_types[product_step] == 'T-03':
            for name in self.T3_machine:
                pp_time = 30
                if self.T3_machine[name][1] == product_idx:
                    pp_time = 0
                early_time = min(early_time, self.T3_machine[name][0] + pp_time)
        else:
            for name in self.T4_machine:
                pp_time = 30
                if self.T4_machine[name][1] == product_idx:
                    pp_time = 0
                early_time = min(early_time, self.T4_machine[name][0] + pp_time)
        time = max(product_time, early_time) + B_duration
        B_start_time = time - B_duration
        B_end_time = time
        # 找浪费时间最少的机器
        waste_time = 10000000
        machine_name = -1
        if route.equip_types[product_step] == 'T-03':
            for name in self.T3_machine:
                pp_time = 30
                if self.T3_machine[name][1] == product_idx:
                    pp_time = 0
                if self.T3_machine[name][0] + pp_time <= B_start_time:
                    if B_start_time - self.T3_machine[name][0] < waste_time:
                        waste_time = B_start_time - self.T3_machine[name][0]
                        machine_name = name
        else:
            for name in self.T4_machine:
                pp_time = 30
                if self.T4_machine[name][1] == product_idx:
                    pp_time = 0
                if self.T4_machine[name][0] + pp_time <= B_start_time:
                    if B_start_time - self.T4_machine[name][0] < waste_time:
                        waste_time = B_start_time - self.T4_machine[name][0]
                        machine_name = name
        if route.equip_types[product_step] == 'T-03':
            self.T3_machine[machine_name] = [B_end_time, product_idx]
        else:
            self.T4_machine[machine_name] = [B_end_time, product_idx]
        self.product_dispatchs.append(
            Period(product.product_id, product_step + 1, machine_name, B_start_time, B_duration, B_end_time))
        self.product_status[product_status_idx][1] += 1
        self.product_status[product_status_idx][2] = time

        p_index = self.product_status[product_status_idx][1]

        if p_index + 1 < len(route.cost_time_low) and route.equip_types[p_index + 1] == 'T-05':
            self.update_D_machine_immediate(product_status_idx)
        else:
            start_time = self.insert_C_task(B_end_time, product.section_num)
            for i in range(product.section_num):
                self.product_dispatchs.append(
                    Period(product.product_id, product_step + 2, 'Y-2045', start_time, 12,
                           start_time + 12))
                start_time += 12
            self.product_status[product_status_idx][1] += 1
            self.product_status[product_status_idx][2] = start_time
        if self.product_status[product_status_idx][1] == len(route.equip_types):
            self.product_status[product_status_idx][0] = 2

    def insert_C_task(self, start_time, section_num):
        pos = -1
        C_duration = section_num * 12
        for i in range(1, len(self.T2_machine)):
            if self.T2_machine[i][0] - C_duration >= max(self.T2_machine[i - 1][1], start_time):
                pos = i
                break
        start_time = max(self.T2_machine[pos - 1][1], start_time)
        self.T2_machine.insert(pos, (start_time, start_time + C_duration))
        return start_time

    def update_D_machine_immediate(self, product_status_idx):
        _, step, time, idx = self.product_status[product_status_idx]
        product = self.data.products[idx]
        route = self.data.routes[self.mp[product.route_id]]
        duration = route.cost_time_low[step+1]
        final_time = 0

        time = self.insert_C_task(time, product.section_num)
        time_list = []
        for i in range(product.section_num):
            self.product_dispatchs.append(
                Period(product.product_id, step + 1, 'Y-2045', time, 12, time + 12))
            time += 12
            time_list.append(time)

        for i in range(product.section_num):
            machine_name = -1
            start_time = 1000000000
            pos = -1
            for name in self.T5_machine:
                tmp_pos = -1
                for j in range(1, len(self.T5_machine[name][1])):
                    if self.T5_machine[name][1][j][0] - duration >= max(self.T5_machine[name][1][j - 1][1], time_list[i]):
                        tmp_pos = j
                        break
                if max(self.T5_machine[name][1][tmp_pos - 1][1], time_list[i]) < start_time:
                    start_time = max(self.T5_machine[name][1][tmp_pos - 1][1], time_list[i])
                    machine_name = name
                    pos = tmp_pos
            self.T5_machine[machine_name][0] = start_time + duration
            self.T5_machine[machine_name][1].insert(pos, (start_time, start_time + duration))
            self.product_dispatchs.append(
                Period(product.product_id, step + 2, machine_name, start_time, duration, start_time + duration))
            final_time = max(final_time, start_time + duration)
        self.product_status[product_status_idx][1] += 2
        self.product_status[product_status_idx][2] = final_time

    def update_D_machine(self):
        for product_status_idx in self.T5_task_list:
            self.update_D_machine_immediate(product_status_idx)

    # 按序安排 启发式贪心
    # status 0未开始 1已开始 2结束
    # (status, step, now_time, idx)
    def solve(self):
        for i in range(len(self.order)):
            idx = self.order[i]
            if i % 2 == 0:
                self.product_dispatchs.append(
                    Period(self.data.products[idx].product_id, 1, 'Z-1001', 240 * (i // 2), 240, 240 * ((i // 2) + 1)))
            else:
                self.product_dispatchs.append(
                    Period(self.data.products[idx].product_id, 1, 'Z-1002', 240 * (i // 2), 240, 240 * ((i // 2) + 1)))
            self.product_status.append([0, 1, 240 * ((i // 2) + 1), idx])
        # now_time = 0
        while not self.is_finished(self.product_status):
            product_status_idx = self.find_work_product()
            self.update_status(product_status_idx)
        # self.update_D_machine()
        self.product_dispatchs.sort(key=lambda x: (x.product_id, x.route_id, x.start_time))
        ans = 0
        for i in range(len(self.product_dispatchs)):
            ans = max(ans, self.product_dispatchs[i].end_time)
        return ans

    def write_ans(self):
        rows = list()
        rows.append('product_id,route_No,equ_name,start,duration,end')
        for i in range(len(self.product_dispatchs)):
            rows += self.product_dispatchs[i].write_ans()
        # for row in rows:
        #     print(row)
        f = open(self.output_path, 'w', encoding='utf-8')
        for row in rows:
            f.write(row + '\n')
        f.close()


if __name__ == '__main__':
    file_path = '../data/tsy_sol.csv'
    solver = Greedy(product_path="../data/产品信息.csv",
                 equip_path="../data/设备信息.csv",
                 route_path="../data/工艺路线.csv",
                 output_path=file_path,)
    val = solver.solve()
    solver.write_ans()
    verifier = Verifier(product_path="../data/产品信息.csv",
                 equip_path="../data/设备信息.csv",
                 route_path="../data/工艺路线.csv")
    verifier.verify(sol_path=file_path)
    verifier.score(sol_path=file_path)
    print(val)
